package com.hxwm.hwdbtool.controller;

import com.hxwm.hwdbtool.controller.menu.SqlEditorMainMenuUI;
import com.hxwm.hwdbtool.controller.menu.SqlEditorToolBarMenuUI;
import com.hxwm.hwdbtool.entity.dbconnect.HwDateSource;
import com.hxwm.hwdbtool.util.HwUtil;
import com.hxwm.hwdbtool.util.JavaFXUIControlUtil;
import com.hxwm.hwdbtool.util.SqlEditorUtil;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.input.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.stage.FileChooser;
import javafx.stage.Window;
import lombok.Data;
import org.apache.log4j.Logger;
import org.fxmisc.richtext.CodeArea;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

/**
 * @program: hwdbtool
 * @ClassName SqlEditorController
 * @description:
 * @author: hxwm
 * @create: 2024-05-27 23:05
 * @Version 1.0
 **/
@Data
public class SqlEditorController {
    private Logger logger = Logger.getLogger(SqlEditorController.class);
    @FXML
    private CodeArea sqlEditorCodeArea;
    @FXML
    private ToolBar sqlEditorToolBar;
    @FXML
    private HBox sqlEditorHBox;
    @FXML
    private Button singleExecuteBtn;
    @FXML
    private Button batchExecuteBtn;

    private HwDBToolController parentController;
    private boolean showLineNumbers = true;
    private ContextMenu codeAreaContextMenu;
    private ContextMenu lineNumberContextMenu;

    private static final String codeAreaContextMenuFxml = "/fxml/menu/SqlEditorMenuUI.fxml";
    private static final String lineNumberContextMenuFxml = "/fxml/menu/SqlEditorToolBarMenuUI.fxml";
    public void initialize() {
        // 创建CodeArea并设置行号工厂：
        sqlEditorCodeArea.setParagraphGraphicFactory(this::lineNumberFactory);
        //sqlEditorCodeArea.setParagraphGraphicFactory(LineNumberFactory.get(sqlEditorCodeArea));
        //sqlEditorCodeArea.setParagraphGraphicFactory(new CustomLineNumberFactory(sqlEditorCodeArea));

        // 添加文本监听器来应用高亮样式：
        sqlEditorCodeArea.textProperty().addListener((obs, oldText, newText) -> {
            sqlEditorCodeArea.setStyleSpans(0, SqlEditorUtil.computeHighlighting(newText));
        });

        // 设置右键菜单事件
        FXMLLoader codeAreaMenuFxmlLoader = new FXMLLoader(getClass().getResource(codeAreaContextMenuFxml));
        FXMLLoader lineNumberMenuFxmlLoader = new FXMLLoader(getClass().getResource(lineNumberContextMenuFxml));
        try {
            codeAreaMenuFxmlLoader.load();
            lineNumberMenuFxmlLoader.load();

            SqlEditorMainMenuUI sqlEditorMainMenuUI = codeAreaMenuFxmlLoader.getController();
            SqlEditorToolBarMenuUI sqlEditorToolBarMenuUI = lineNumberMenuFxmlLoader.getController();

            codeAreaContextMenu = sqlEditorMainMenuUI.getSqlEditorMainMenu();
            lineNumberContextMenu = sqlEditorToolBarMenuUI.getSqlEditorToolBarMenu();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        // 编辑器右键菜单
        sqlEditorCodeArea.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
            if (event.getButton() == MouseButton.SECONDARY) {
                if(isClickInsideNode(sqlEditorCodeArea, event)) {
                    codeAreaContextMenu.show(sqlEditorCodeArea, event.getScreenX(), event.getScreenY());
                }
            }else{
                codeAreaContextMenu.hide();
            }
        });

        // 设置 Ctrl + S 快捷键
        sqlEditorCodeArea.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
            final KeyCombination keyComb = new KeyCodeCombination(KeyCode.S, KeyCombination.CONTROL_DOWN);
            if (keyComb.match(event)) {
                saveToFile(sqlEditorHBox.getParent().getScene().getWindow(), sqlEditorCodeArea);
                event.consume();
            }
        });
        singleExecuteBtn.setOnAction(e->{
            String selectedText = sqlEditorCodeArea.getSelectedText();
            if(HwUtil.isNullOrEmpty(selectedText)){
                JavaFXUIControlUtil.showInformationDialog("提示","请选择要执行的语句!");
                return;
            }
            singleSqlExecute(selectedText);
        });
        batchExecuteBtn.setOnAction(e->{
            String selectedText = sqlEditorCodeArea.getSelectedText();
            if(HwUtil.isNullOrEmpty(selectedText)){
                selectedText = sqlEditorCodeArea.getText();
            }
            sqlScriptExecute(selectedText);
        });
    }

    private void singleSqlExecute(String sql){
        //非空且合法的sql，才能去执行
        if(!HwUtil.isNullOrEmpty(sql) && SqlEditorUtil.isSQLValid(sql)){
            //获取当前选中数据源
            HwDateSource currentHwDataSource = parentController.getCurrentHwDataSource();
            if(!HwUtil.isNullOrEmpty(currentHwDataSource)){
                try(Statement statement = currentHwDataSource.getConnection().createStatement()){
                    //获取语句关键字 判断是否查询语句
                    if(HwUtil.startWithSingleWordIgnoreCase("select",sql)){
                        //创建tabview数据容器
                        TableView<ObservableList<String>> tableView = new TableView<>();
                        //执行查询
                        ResultSet resultSet = statement.executeQuery(sql);
                        ResultSetMetaData metaData = resultSet.getMetaData();
                        int columnCount = metaData.getColumnCount();
                        List<TableColumn<ObservableList<String>,String>> columns = new ArrayList<>();
                        for (int i = 1; i <= metaData.getColumnCount(); i++) {
                            //获取结果集中的列名
                            TableColumn<ObservableList<String>,String> column = new TableColumn<>(metaData.getColumnName(i));
                            final int colIndex = i - 1;
                            //创建tableview的列
                            column.setCellValueFactory(cellData-> new SimpleStringProperty(cellData.getValue().get(colIndex)));
                            columns.add(column);
                        }
                        tableView.getColumns().addAll(columns);
                        //遍历结果集展示在tableview
                        while (resultSet.next()){
                            ObservableList<String> row = FXCollections.observableArrayList();
                            for (int i = 1; i <= columnCount; i++) {
                                row.add(resultSet.getString(i));
                            }
                            tableView.getItems().add(row);
                        }
                        //创建tab，添加结果集到tab
                        Tab tab = new Tab("result");
                        tab.setContent(tableView);
                        parentController.getControlTabPane().getTabs().add(tab);
                    }else{
                        boolean execute = statement.execute(sql);
                        logger.info("执行成功!");
                    }
                }catch (Exception e){
                    logger.error("执行失败!" + e.getMessage());
                }
            }else{
                JavaFXUIControlUtil.showInformationDialog("提示","请选择数据源");
            }

        }
    }

    private void sqlScriptExecute(String sqlScript){

    }
    /**
     * 行号工厂
     * @param lineNumber 行号
     * @return 带有行号的标签
     */
    private Node lineNumberFactory(int lineNumber) {
        if (!showLineNumbers) {
            return new StackPane(); // 返回空白区域
        }
        lineNumber ++;
        Label lineNo = new Label(String.format("%4d", lineNumber));
        lineNo.getStyleClass().add("lineNo");
        lineNo.setFont(Font.font("monospace", 10)); // 设置字体和大小
        lineNo.setTextFill(Color.GRAY); // 设置行号颜色

        StackPane stack = new StackPane(lineNo);
        stack.setPadding(new Insets(0, 10, 0, 0)); // 设置右侧的填充以增加间距

        lineNo.setOnMouseClicked(event -> {
            if (event.getButton() == MouseButton.SECONDARY) {
                codeAreaContextMenu.hide();
                lineNumberContextMenu.show(lineNo, event.getScreenX(), event.getScreenY());
            }
        });
        return stack;
    }
    /**
     * 判断鼠标点击是否在指定Node区域内
     * @param node
     * @param event
     * @return
     */
    private boolean isClickInsideNode(Node node, MouseEvent event) {
        double x = event.getX();
        double y = event.getY();
        double nodeMinX = node.getLayoutX();
        double nodeMinY = node.getLayoutY();
        double nodeMaxX = nodeMinX + node.getBoundsInParent().getWidth();
        double nodeMaxY = nodeMinY + node.getBoundsInParent().getHeight();
        return x >= nodeMinX && x <= nodeMaxX && y >= nodeMinY && y <= nodeMaxY;
    }

    /**
     * 文件保存
     * @param window
     * @param codeArea
     */
    private void saveToFile(Window window, CodeArea codeArea) {
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("Save Script");
        fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Sql Script", "*.sql"));
        File file = fileChooser.showSaveDialog(window);
        if (file != null) {
            try (FileWriter writer = new FileWriter(file)) {
                writer.write(codeArea.getText());
            } catch (IOException e) {
                Alert alert = new Alert(Alert.AlertType.ERROR);
                alert.setTitle("Error");
                alert.setHeaderText("Could not save file");
                alert.setContentText(e.getMessage());
                alert.showAndWait();
            }
        }
    }
}
